Android实现文本逐字显示View(类似rpg游戏人物对话,文本逐字显示)

前面好多篇文章都是Android Studio、源码编译、ndk等相关教程,今天敲一敲代码,不然都生锈了哈^_^。

来个古装动画美图,缓解大家疲劳的眼睛…(话说有木有人知道这是谁???)

Paste_Image.png

这是一个类似rpg游戏人物对话,文本逐字显示的view,先上效果图,不然大家都没有兴趣看下去,嘿嘿。

Demo地址:github(喜欢的可以运行看看)
showText3.gif
注:我录制的gif是不是很清晰,想知道我用的是什么工具制作的吗?^_^ (评论中有方法喔)

思路:想让汉字、英文、数字、符号等逐个显示,首先想到的是字符串处理,写一个工具类拆分汉字、英文、数字、符号等,然后就是应用到Android的View上了,View上的实现其实很low,就是先使用工具类拆分一篇文本,然后将使用递归每隔300毫秒(时间可以修改)将文本画到View的Canvas上;大体思路就是这样。

注:不知道有没有已经造好轮子,我在网上没找到类似的。如果大家看过类似的又比较优秀的实现,麻烦在评论里发给我哈。

1.首先写字符串工具类XTextUtils.java,拆分字符串,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/**
* Created by zhangyipeng on 16/3/16.
*/
public class XTextUtils {
private static final String TAG = "XTextUtils";

public static ArrayList<String> getContentList(String content) {
ArrayList<String> list = new ArrayList<>();
String s = "";
String n = "";
int sn = 0;
content+=" ";
int len = (content).length();

for (int i = 0; i < len; i++) {

char c = content.charAt(i);
char c_1 = ' ';
if(i>=1) {
c_1 = content.charAt(i - 1);
}
if (isEnglish(c + "") || (isEnglish(c_1+"") && (c+"").equals("'"))) {
s += c;
sn = 1;
}else if (isNumber(c + "") || (isNumber(c_1+"") && (c+"").equals("."))) {
n += c;
sn = 2;
} else {
if (!s.equals("") && !n.equals("") & sn==1) {
list.add(n+s);

}else if (!s.equals("") && !n.equals("") & sn==2) {
list.add(s+n);

}else if (!s.equals("") && n.equals("")) {
list.add(s);
}else if (!n.equals("") && s.equals("")) {
list.add(n);
}
list.add(c + "");
sn = 0;
n = "";
s = "";

}
}
return list;
}

public static boolean isEnglish(String s) {

Pattern p = Pattern.compile("^[a-zA-Z]*$");
Matcher m = p.matcher(s);
if (m.matches()) {
return true;
} else {
return false;
}
}


public static boolean isNumber(String s) {

Pattern p = Pattern.compile("^[0-9]*$");
Matcher m = p.matcher(s);
if (m.matches()) {
return true;
} else {
return false;
}
}

public static boolean isChinese(String s) {

Pattern p = Pattern.compile("^[\\u4E00-\\u9FA5\\uF900-\\uFA2D]*$");
Matcher m = p.matcher(s);
if (m.matches()) {
return true;
} else {
return false;
}
}
}

我们使用网上的任意一篇文章测试,这个工具类,效果如下图:

1
2
3
4
5
6
胡|歌|,|1982|年|9|月|20|日|出|生|于|上|海|市|徐|汇|区|,|中|国|内|地|演|员|、|歌|手|、|制|片|人|。|1996|年|,|14|岁|的|胡|歌|便|成|为|上|海|教|育|电|视|台|的|小|主|持|人|,|2001|年|考|入|上|海|戏|剧|学|院|表|演|系|。|2005|年|因|在|电|视|剧|《|仙|剑|奇|侠|传|》|中|成|功|塑|造|了|豪|爽|深|情|的|“|李|逍|遥|”|一|角|而|成|名|,|并|演|唱|插|曲|《|六|月|的|雨|》|《|逍|遥|叹|》|。|2009|年|在|“|80|后|新|生|代|娱|乐|大|明|星|”|评|选|中|获|封|“|四|大|小|生|”|之|一|。|
|First| |Flight|
|Mr|.| |Johnson| |had| |never| |been| |up| |in| |an| |aerophane| |before| |and| |he| |had| |read| |a| |lot| |about| |air| |accidents|,| |so| |one| |day| |when| |a| |friend| |offered| |to| |take| |him| |for| |a| |ride| |in| |his| |own| |small| |phane|,| |Mr|.| |Johnson| |was| |very| |worried| |about| |accepting|.| |Finally|,| |however|,| |his| |friend| |persuaded| |him| |that| |it| |was| |very| |safe|,| |and| |Mr|.| |Johnson| |boarded| |the| |plane|.|
|His| |friend| |started| |the| |engine| |and| |began| |to| |taxi| |onto| |the| |runway| |of| |the| |airport|.| |Mr|.| |Johnson| |had| |heard| |that| |the| |most| |dangerous| |part| |of| |a| |flight| |were| |the| |take|-|off| |and| |the| |landing|,| |so| |he| |was| |extremely| |frightened| |and| |closed| |his| |eyes|.|
|After| |a| |minute| |or| |two| |he| |opened| |them| |again|,| |looked| |out| |of| |the| |window| |of| |the| |plane|,| |and| |said| |to| |his| |friend|,| |"|Look| |at| |those| |people| |down| |there|.| |They| |look| |as| |small| |as| |ants|,| |don't| |they|?|"|
|"|Those| |are| |ants|,|"| |answered| |his| |friend|.| |"|We're| |still| |on| |the| |ground|.|"| |

Paste_Image.png

2.好了下面开始自定义View类的coding了,代码如下:

注:如果直接使用canvas.drawText()的话,很难根据屏幕的宽度自动换行,还好Andorid为我们提供了StaticLayout类,使用它可以轻松的实现文本自动换行,以及行间距、对其方式(居左对齐、居中对其、居右对齐)等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* Created by zhangyipeng on 16/3/16.
*/

public class XTextView extends View {

private TextPaint textPaint;
private float density;
private String textContent;
private int textColor;
private String textAlignment;
private float textSize;
private float textSpacingAdd;
private float textSpacingMult;

public XTextView(Context context) {
this(context, null, 0);
}

public XTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public XTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.XTextView);
textContent = a.getString(R.styleable.XTextView_textContent);
textColor = a.getColor(R.styleable.XTextView_textColor, Color.BLACK);
textAlignment = a.getString(R.styleable.XTextView_textXAlignment);
textSize = a.getDimension(R.styleable.XTextView_textSize, 20);
textSpacingAdd = a.getFloat(R.styleable.XTextView_textSpacingAdd, 0.0F);
textSpacingMult = a.getFloat(R.styleable.XTextView_textSpacingMult, 1.0F);


init();
}


public void setTextSize(float textSize) {
this.textSize = textSize;
}

public void setTextSpacingAdd(float textSpacingAdd) {
this.textSpacingAdd = textSpacingAdd;
}

public void setTextSpacingMult(float textSpacingMult) {
this.textSpacingMult = textSpacingMult;
}

public void setTextColor(int textColor) {
this.textColor = textColor;
}

public void setTextAlignment(String textAlignment) {
this.textAlignment = textAlignment;
}

public void setTextContent(final String content) {
new Thread(){
@Override
public void run() {
super.run();
contents = XTextUtils.getContentList(content);
}
}.run();

}

private ArrayList<String> contents;

private void init() {

density = getResources().getDisplayMetrics().density;

textPaint = new TextPaint();
textPaint.setColor(textColor);
textPaint.setTextSize(textSize);


}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

drawText(canvas);
}

private int cnt = 0;
private String totalText = "";

private void drawText(Canvas canvas) {

if (cnt >= contents.size()) {
return;
}
totalText += contents.get(cnt);
StaticLayout layout = null;
if (textAlignment.equals("normal")) {
//textPaint(TextPaint 类型)设置了字符串格式及属性的画笔,240为设置画多宽后换行,后面的参数是对齐方式及行间距
layout = new StaticLayout(totalText, textPaint, getWidth() - (int) (20 * density), Layout.Alignment.ALIGN_NORMAL, textSpacingMult, textSpacingAdd, true);
} else if (textAlignment.equals("center")) {
layout = new StaticLayout(totalText, textPaint, getWidth() - (int) (20 * density), Layout.Alignment.ALIGN_CENTER, textSpacingMult, textSpacingAdd, true);
} else if (textAlignment.equals("opposite")) {
layout = new StaticLayout(totalText, textPaint, getWidth() - (int) (20 * density), Layout.Alignment.ALIGN_OPPOSITE, textSpacingMult, textSpacingAdd, true);

}

//从 (10,10)的位置开始绘制
canvas.translate(10 * density, 10 * density);

layout.draw(canvas);

cnt++;

startText();
}

public void startText() {
if (cnt != contents.size()) {

new Handler().postDelayed(new Runnable() {
@Override
public void run() {
invalidate();
}
}, time);
}
}

private long time = 200;

public void setDelayPlayTime(long time) {
this.time = time;
}
}

3.接下来就是调用了,运行了,如下图

layout文件如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.zhangyipeng.graduallyshowtext.TargetActivity">


<com.example.zhangyipeng.gradually_show_text.view.XTextView
android:id="@+id/xtv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:textXAlignment="normal"
app:textColor="#ff003b"
app:textSize="20sp"
app:textContent="你好"
app:textSpacingMult="1.5"
app:textSpacingAdd="0.0"
/>

</RelativeLayout>

Paste_Image.png

4.运行效果如下(再贴一次哈):
showText3.gif
Demo地址:github